home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / unix / tclUnixNotfy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  13.1 KB  |  518 lines  |  [TEXT/CWIE]

  1. /*
  2.  * tclUnixNotify.c --
  3.  *
  4.  *    This file contains the implementation of the select-based
  5.  *    Unix-specific notifier, which is the lowest-level part of the
  6.  *    Tcl event loop.  This file works together with
  7.  *    ../generic/tclNotify.c.
  8.  *
  9.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tclUnixNotfy.c 1.42 97/07/02 20:55:44
  15.  */
  16.  
  17. #include "tclInt.h"
  18. #include "tclPort.h"
  19. #include <signal.h> 
  20.  
  21. /*
  22.  * This structure is used to keep track of the notifier info for a 
  23.  * a registered file.
  24.  */
  25.  
  26. typedef struct FileHandler {
  27.     int fd;
  28.     int mask;            /* Mask of desired events: TCL_READABLE,
  29.                  * etc. */
  30.     int readyMask;        /* Mask of events that have been seen since the
  31.                  * last time file handlers were invoked for
  32.                  * this file. */
  33.     Tcl_FileProc *proc;        /* Procedure to call, in the style of
  34.                  * Tcl_CreateFileHandler. */
  35.     ClientData clientData;    /* Argument to pass to proc. */
  36.     struct FileHandler *nextPtr;/* Next in list of all files we care about. */
  37. } FileHandler;
  38.  
  39. /*
  40.  * The following structure is what is added to the Tcl event queue when
  41.  * file handlers are ready to fire.
  42.  */
  43.  
  44. typedef struct FileHandlerEvent {
  45.     Tcl_Event header;        /* Information that is standard for
  46.                  * all events. */
  47.     int fd;            /* File descriptor that is ready.  Used
  48.                  * to find the FileHandler structure for
  49.                  * the file (can't point directly to the
  50.                  * FileHandler structure because it could
  51.                  * go away while the event is queued). */
  52. } FileHandlerEvent;
  53.  
  54. /*
  55.  * The following static structure contains the state information for the
  56.  * select based implementation of the Tcl notifier.
  57.  */
  58.  
  59. static struct {
  60.     FileHandler *firstFileHandlerPtr;
  61.                 /* Pointer to head of file handler list. */
  62.     fd_mask checkMasks[3*MASK_SIZE];
  63.                 /* This array is used to build up the masks
  64.                  * to be used in the next call to select.
  65.                  * Bits are set in response to calls to
  66.                  * Tcl_CreateFileHandler. */
  67.     fd_mask readyMasks[3*MASK_SIZE];
  68.                 /* This array reflects the readable/writable
  69.                  * conditions that were found to exist by the
  70.                  * last call to select. */
  71.     int numFdBits;        /* Number of valid bits in checkMasks
  72.                  * (one more than highest fd for which
  73.                  * Tcl_WatchFile has been called). */
  74. } notifier;
  75.  
  76. /*
  77.  * The following static indicates whether this module has been initialized.
  78.  */
  79.  
  80. static int initialized = 0;
  81.  
  82. /*
  83.  * Static routines defined in this file.
  84.  */
  85.  
  86. static void        InitNotifier _ANSI_ARGS_((void));
  87. static void        NotifierExitHandler _ANSI_ARGS_((
  88.                 ClientData clientData));
  89. static int        FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  90.                 int flags));
  91.  
  92. /*
  93.  *----------------------------------------------------------------------
  94.  *
  95.  * InitNotifier --
  96.  *
  97.  *    Initializes the notifier state.
  98.  *
  99.  * Results:
  100.  *    None.
  101.  *
  102.  * Side effects:
  103.  *    Creates a new exit handler.
  104.  *
  105.  *----------------------------------------------------------------------
  106.  */
  107.  
  108. static void
  109. InitNotifier()
  110. {
  111.     initialized = 1;
  112.     memset(¬ifier, 0, sizeof(notifier));
  113.     Tcl_CreateExitHandler(NotifierExitHandler, NULL);
  114. }
  115.  
  116. /*
  117.  *----------------------------------------------------------------------
  118.  *
  119.  * NotifierExitHandler --
  120.  *
  121.  *    This function is called to cleanup the notifier state before
  122.  *    Tcl is unloaded.
  123.  *
  124.  * Results:
  125.  *    None.
  126.  *
  127.  * Side effects:
  128.  *    Destroys the notifier window.
  129.  *
  130.  *----------------------------------------------------------------------
  131.  */
  132.  
  133. static void
  134. NotifierExitHandler(clientData)
  135.     ClientData clientData;        /* Not used. */
  136. {
  137.     initialized = 0;
  138. }
  139.  
  140. /*
  141.  *----------------------------------------------------------------------
  142.  *
  143.  * Tcl_SetTimer --
  144.  *
  145.  *    This procedure sets the current notifier timer value.  This
  146.  *    interface is not implemented in this notifier because we are
  147.  *    always running inside of Tcl_DoOneEvent.
  148.  *
  149.  * Results:
  150.  *    None.
  151.  *
  152.  * Side effects:
  153.  *    None.
  154.  *
  155.  *----------------------------------------------------------------------
  156.  */
  157.  
  158. void
  159. Tcl_SetTimer(timePtr)
  160.     Tcl_Time *timePtr;        /* Timeout value, may be NULL. */
  161. {
  162.     /*
  163.      * The interval timer doesn't do anything in this implementation,
  164.      * because the only event loop is via Tcl_DoOneEvent, which passes
  165.      * timeout values to Tcl_WaitForEvent.
  166.      */
  167. }
  168.  
  169. /*
  170.  *----------------------------------------------------------------------
  171.  *
  172.  * Tcl_CreateFileHandler --
  173.  *
  174.  *    This procedure registers a file handler with the Xt notifier.
  175.  *
  176.  * Results:
  177.  *    None.
  178.  *
  179.  * Side effects:
  180.  *    Creates a new file handler structure and registers one or more
  181.  *    input procedures with Xt.
  182.  *
  183.  *----------------------------------------------------------------------
  184.  */
  185.  
  186. void
  187. Tcl_CreateFileHandler(fd, mask, proc, clientData)
  188.     int fd;            /* Handle of stream to watch. */
  189.     int mask;            /* OR'ed combination of TCL_READABLE,
  190.                  * TCL_WRITABLE, and TCL_EXCEPTION:
  191.                  * indicates conditions under which
  192.                  * proc should be called. */
  193.     Tcl_FileProc *proc;        /* Procedure to call for each
  194.                  * selected event. */
  195.     ClientData clientData;    /* Arbitrary data to pass to proc. */
  196. {
  197.     FileHandler *filePtr;
  198.     int index, bit;
  199.     
  200.     if (!initialized) {
  201.     InitNotifier();
  202.     }
  203.  
  204.     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
  205.         filePtr = filePtr->nextPtr) {
  206.     if (filePtr->fd == fd) {
  207.         break;
  208.     }
  209.     }
  210.     if (filePtr == NULL) {
  211.     filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); /* MLK */
  212.     filePtr->fd = fd;
  213.     filePtr->readyMask = 0;
  214.     filePtr->nextPtr = notifier.firstFileHandlerPtr;
  215.     notifier.firstFileHandlerPtr = filePtr;
  216.     }
  217.     filePtr->proc = proc;
  218.     filePtr->clientData = clientData;
  219.     filePtr->mask = mask;
  220.  
  221.     /*
  222.      * Update the check masks for this file.
  223.      */
  224.  
  225.     index = fd/(NBBY*sizeof(fd_mask));
  226.     bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
  227.     if (mask & TCL_READABLE) {
  228.     notifier.checkMasks[index] |= bit;
  229.     } else {
  230.     notifier.checkMasks[index] &= ~bit;
  231.     } 
  232.     if (mask & TCL_WRITABLE) {
  233.     (notifier.checkMasks+MASK_SIZE)[index] |= bit;
  234.     } else {
  235.     (notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
  236.     }
  237.     if (mask & TCL_EXCEPTION) {
  238.     (notifier.checkMasks+2*(MASK_SIZE))[index] |= bit;
  239.     } else {
  240.     (notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
  241.     }
  242.     if (notifier.numFdBits <= fd) {
  243.     notifier.numFdBits = fd+1;
  244.     }
  245. }
  246.  
  247. /*
  248.  *----------------------------------------------------------------------
  249.  *
  250.  * Tcl_DeleteFileHandler --
  251.  *
  252.  *    Cancel a previously-arranged callback arrangement for
  253.  *    a file.
  254.  *
  255.  * Results:
  256.  *    None.
  257.  *
  258.  * Side effects:
  259.  *    If a callback was previously registered on file, remove it.
  260.  *
  261.  *----------------------------------------------------------------------
  262.  */
  263.  
  264. void
  265. Tcl_DeleteFileHandler(fd)
  266.     int fd;        /* Stream id for which to remove callback procedure. */
  267. {
  268.     FileHandler *filePtr, *prevPtr;
  269.     int index, bit, mask, i;
  270.  
  271.     if (!initialized) {
  272.     InitNotifier();
  273.     }
  274.  
  275.     /*
  276.      * Find the entry for the given file (and return if there
  277.      * isn't one).
  278.      */
  279.  
  280.     for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
  281.         prevPtr = filePtr, filePtr = filePtr->nextPtr) {
  282.     if (filePtr == NULL) {
  283.         return;
  284.     }
  285.     if (filePtr->fd == fd) {
  286.         break;
  287.     }
  288.     }
  289.  
  290.     /*
  291.      * Update the check masks for this file.
  292.      */
  293.  
  294.     index = fd/(NBBY*sizeof(fd_mask));
  295.     bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
  296.  
  297.     if (filePtr->mask & TCL_READABLE) {
  298.     notifier.checkMasks[index] &= ~bit;
  299.     }
  300.     if (filePtr->mask & TCL_WRITABLE) {
  301.     (notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
  302.     }
  303.     if (filePtr->mask & TCL_EXCEPTION) {
  304.     (notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
  305.     }
  306.  
  307.     /*
  308.      * Find current max fd.
  309.      */
  310.  
  311.     if (fd+1 == notifier.numFdBits) {
  312.     for (notifier.numFdBits = 0; index >= 0; index--) {
  313.         mask = notifier.checkMasks[index]
  314.         | (notifier.checkMasks+MASK_SIZE)[index]
  315.         | (notifier.checkMasks+2*(MASK_SIZE))[index];
  316.         if (mask) {
  317.         for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) {
  318.             if (mask & (1 << (i-1))) {
  319.             break;
  320.             }
  321.         }
  322.         notifier.numFdBits = index * (NBBY*sizeof(fd_mask)) + i;
  323.         break;
  324.         }
  325.     }
  326.     }
  327.  
  328.     /*
  329.      * Clean up information in the callback record.
  330.      */
  331.  
  332.     if (prevPtr == NULL) {
  333.     notifier.firstFileHandlerPtr = filePtr->nextPtr;
  334.     } else {
  335.     prevPtr->nextPtr = filePtr->nextPtr;
  336.     }
  337.     ckfree((char *) filePtr);
  338. }
  339.  
  340. /*
  341.  *----------------------------------------------------------------------
  342.  *
  343.  * FileHandlerEventProc --
  344.  *
  345.  *    This procedure is called by Tcl_ServiceEvent when a file event
  346.  *    reaches the front of the event queue.  This procedure is
  347.  *    responsible for actually handling the event by invoking the
  348.  *    callback for the file handler.
  349.  *
  350.  * Results:
  351.  *    Returns 1 if the event was handled, meaning it should be removed
  352.  *    from the queue.  Returns 0 if the event was not handled, meaning
  353.  *    it should stay on the queue.  The only time the event isn't
  354.  *    handled is if the TCL_FILE_EVENTS flag bit isn't set.
  355.  *
  356.  * Side effects:
  357.  *    Whatever the file handler's callback procedure does.
  358.  *
  359.  *----------------------------------------------------------------------
  360.  */
  361.  
  362. static int
  363. FileHandlerEventProc(evPtr, flags)
  364.     Tcl_Event *evPtr;        /* Event to service. */
  365.     int flags;            /* Flags that indicate what events to
  366.                  * handle, such as TCL_FILE_EVENTS. */
  367. {
  368.     FileHandler *filePtr;
  369.     FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
  370.     int mask;
  371.  
  372.     if (!(flags & TCL_FILE_EVENTS)) {
  373.     return 0;
  374.     }
  375.  
  376.     /*
  377.      * Search through the file handlers to find the one whose handle matches
  378.      * the event.  We do this rather than keeping a pointer to the file
  379.      * handler directly in the event, so that the handler can be deleted
  380.      * while the event is queued without leaving a dangling pointer.
  381.      */
  382.  
  383.     for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
  384.         filePtr = filePtr->nextPtr) {
  385.     if (filePtr->fd != fileEvPtr->fd) {
  386.         continue;
  387.     }
  388.  
  389.     /*
  390.      * The code is tricky for two reasons:
  391.      * 1. The file handler's desired events could have changed
  392.      *    since the time when the event was queued, so AND the
  393.      *    ready mask with the desired mask.
  394.      * 2. The file could have been closed and re-opened since
  395.      *    the time when the event was queued.  This is why the
  396.      *    ready mask is stored in the file handler rather than
  397.      *    the queued event:  it will be zeroed when a new
  398.      *    file handler is created for the newly opened file.
  399.      */
  400.  
  401.     mask = filePtr->readyMask & filePtr->mask;
  402.     filePtr->readyMask = 0;
  403.     if (mask != 0) {
  404.         (*filePtr->proc)(filePtr->clientData, mask);
  405.     }
  406.     break;
  407.     }
  408.     return 1;
  409. }
  410.  
  411. /*
  412.  *----------------------------------------------------------------------
  413.  *
  414.  * Tcl_WaitForEvent --
  415.  *
  416.  *    This function is called by Tcl_DoOneEvent to wait for new
  417.  *    events on the message queue.  If the block time is 0, then
  418.  *    Tcl_WaitForEvent just polls without blocking.
  419.  *
  420.  * Results:
  421.  *    Returns -1 if the select would block forever, otherwise
  422.  *    returns 0.
  423.  *
  424.  * Side effects:
  425.  *    Queues file events that are detected by the select.
  426.  *
  427.  *----------------------------------------------------------------------
  428.  */
  429.  
  430. int
  431. Tcl_WaitForEvent(timePtr)
  432.     Tcl_Time *timePtr;        /* Maximum block time, or NULL. */
  433. {
  434.     FileHandler *filePtr;
  435.     FileHandlerEvent *fileEvPtr;
  436.     struct timeval timeout, *timeoutPtr;
  437.     int bit, index, mask, numFound;
  438.  
  439.     if (!initialized) {
  440.     InitNotifier();
  441.     }
  442.  
  443.     /*
  444.      * Set up the timeout structure.  Note that if there are no events to
  445.      * check for, we return with a negative result rather than blocking
  446.      * forever.
  447.      */
  448.  
  449.     if (timePtr) {
  450.     timeout.tv_sec = timePtr->sec;
  451.     timeout.tv_usec = timePtr->usec;
  452.     timeoutPtr = &timeout;
  453.     } else if (notifier.numFdBits == 0) {
  454.     return -1;
  455.     } else {
  456.     timeoutPtr = NULL;
  457.     }
  458.  
  459.     memcpy((VOID *) notifier.readyMasks, (VOID *) notifier.checkMasks,
  460.         3*MASK_SIZE*sizeof(fd_mask));
  461.     numFound = select(notifier.numFdBits,
  462.         (SELECT_MASK *) ¬ifier.readyMasks[0],
  463.         (SELECT_MASK *) ¬ifier.readyMasks[MASK_SIZE],
  464.         (SELECT_MASK *) ¬ifier.readyMasks[2*MASK_SIZE], timeoutPtr);
  465.  
  466.     /*
  467.      * Some systems don't clear the masks after an error, so
  468.      * we have to do it here.
  469.      */
  470.  
  471.     if (numFound == -1) {
  472.     memset((VOID *) notifier.readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
  473.     }
  474.  
  475.     /*
  476.      * Queue all detected file events before returning.
  477.      */
  478.  
  479.     for (filePtr = notifier.firstFileHandlerPtr;
  480.         (filePtr != NULL) && (numFound > 0);
  481.         filePtr = filePtr->nextPtr) {
  482.     index = filePtr->fd / (NBBY*sizeof(fd_mask));
  483.     bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask)));
  484.     mask = 0;
  485.  
  486.     if (notifier.readyMasks[index] & bit) {
  487.         mask |= TCL_READABLE;
  488.     }
  489.     if ((notifier.readyMasks+MASK_SIZE)[index] & bit) {
  490.         mask |= TCL_WRITABLE;
  491.     }
  492.     if ((notifier.readyMasks+2*(MASK_SIZE))[index] & bit) {
  493.         mask |= TCL_EXCEPTION;
  494.     }
  495.  
  496.     if (!mask) {
  497.         continue;
  498.     } else {
  499.         numFound--;
  500.     }
  501.  
  502.     /*
  503.      * Don't bother to queue an event if the mask was previously
  504.      * non-zero since an event must still be on the queue.
  505.      */
  506.  
  507.     if (filePtr->readyMask == 0) {
  508.         fileEvPtr = (FileHandlerEvent *) ckalloc(
  509.         sizeof(FileHandlerEvent));
  510.         fileEvPtr->header.proc = FileHandlerEventProc;
  511.         fileEvPtr->fd = filePtr->fd;
  512.         Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
  513.     }
  514.     filePtr->readyMask = mask;
  515.     }
  516.     return 0;
  517. }
  518.